package com.generic.mybatis.dal.interceptor.automap.builder;

import com.generic.mybatis.dal.dao.criteria.Criteria;
import com.generic.mybatis.dal.dao.page.Pageable;
import com.generic.mybatis.dal.dao.page.Sort;
import com.generic.mybatis.dal.interceptor.automap.FieldMapper;
import com.generic.mybatis.dal.interceptor.automap.TableMapper;
import com.generic.mybatis.dal.util.Reflections;
import com.google.common.collect.Maps;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;

import java.util.Iterator;
import java.util.Map;

/**
 * SqlBuilderUtil 帮忙类
 *
 * @author wuaj
 */
public final class SqlBuilderUtil {
    private SqlBuilderUtil() {
    }

    public static void buildIdWhereStatement(SQL sql, TableMapper tableMapper, Object idValue) {
        if (idValue == null) {
            throw new RuntimeException(" id value is null");
        }
        Map dtoFieldMap;
        if (idValue instanceof Map) {
            dtoFieldMap = (Map) idValue;
        } else {
            if (tableMapper.getIds().size() != 1) {
                throw new RuntimeException(" just allow one id key for non map type");
            }
            dtoFieldMap = Maps.newHashMap();
            dtoFieldMap.put(tableMapper.getIds().get(0), idValue);
        }

        for (String id : tableMapper.getIds()) {
            FieldMapper fieldMapper = tableMapper.getFieldMapperCache().get(id);
            Object value = dtoFieldMap.get(fieldMapper.getFieldName());
            if (value == null) {
                throw new RuntimeException("Unique key '" + id
                        + "' can't be null, build query sql failed!");
            }
            sql.WHERE(String.format("%s.%s = #{%s}", tableMapper.getAlias(),
                    fieldMapper.getDbFieldName(), fieldMapper.getFieldName()));
        }
    }

    public static SQL buildSelectStatement(TableMapper tableMapper) {
        SQL sql = new SQL();
        tableMapper.buildSelectStatement(sql);
        tableMapper.buildFromStatement(sql);
        return sql;
    }

    public static void buildCritialStatement(TableMapper tableMapper, SQL sql,
                                             Object parameterObject, String paramName) {
        if (parameterObject != null && parameterObject instanceof Map) {
            Map paramMap = (Map) parameterObject;
            Criteria[] criterias = (Criteria[]) paramMap.get(paramName);
            if (criterias != null && criterias.length > 0) {
                for (Criteria criteria : criterias) {
                    criteria.toSql(tableMapper, sql, paramMap);
                }
            }
        }
    }


    public static String buildPagableStatement(TableMapper tableMapper, SQL sql, Object parameterObject, String paramName) {
        if (parameterObject != null && parameterObject instanceof Map) {
            Map paramMap = (Map) parameterObject;
            Pageable pageable = (Pageable) paramMap.get(paramName);
            if (pageable != null) {
                if (pageable.getSort() != null) {
                    Iterator<Sort.Order> it = pageable.getSort().iterator();
                    while (it.hasNext()) {
                        Sort.Order order = it.next();
                        sql.ORDER_BY(String.format("%s %s", tableMapper.getPropertyAlias(order.getProperty()), order.getDirection().name()));
                    }
                }
                StringBuilder builder = new StringBuilder();
                builder = sql.usingAppender(builder);
                builder.append(" limit #{pageLimit}  offset #{pageOffset} ");
                paramMap.put("pageOffset", pageable.getOffset());
                paramMap.put("pageLimit", pageable.getPageSize());
                return builder.toString();
            }
        }
        return sql.toString();
    }

    public static void setResultType(MappedStatement mappedStatement, Class dtoClass) {
        ResultMap resultMap = mappedStatement.getResultMaps().get(0);
        Reflections.setFieldValue(resultMap, "type", dtoClass);
//        Reflections.setFieldValue(mappedStatement, "useCache", Boolean.FALSE);
    }

    public static void setKeyGenerater(MappedStatement mappedStatement) {
        Reflections.setFieldValue(mappedStatement, "keyGenerator", new Jdbc3KeyGenerator());
    }


    public static void replaceResultMap(MappedStatement mappedStatement, ResultMap replace) {
        ResultMap resultMap = mappedStatement.getResultMaps().get(0);
        Reflections.setFieldValue(resultMap, "type", replace.getType());
        Reflections.setFieldValue(resultMap, "resultMappings", replace.getResultMappings());
//        Reflections.setFieldValue(resultMap, "idResultMappings", replace.getIdResultMappings());
        Reflections.setFieldValue(resultMap, "propertyResultMappings", replace.getPropertyResultMappings());
        Reflections.setFieldValue(resultMap, "mappedColumns", replace.getMappedColumns());
    }

}
